PythonでLuaを呼び出すときにハマったところ
Luaについて
Luaは軽量なスクリプト言語です。Luaはポータビリティと拡張性を重視して設計されており、他の言語から呼び出しやすいです。今回はPythonでLuaを呼び出せるlupaを使用してみます。
参考: Lua - Wikipedia
Lupa
今回はLupaというパッケージを使用してLuaをPythonから呼び出してみます。
基本的な使い方は以下のリポジトリのReadmeに書いてあるので省きます。 今回は、Lupaを使う上で個人的に分かりにくかったポイントを重点的に解説していきます。
参考: scoder/lupa: Lua in Python
使い方
基本的な使い方
import lupa from lupa import LuaRuntime lua = LuaRuntime(unpack_returned_tuples=True) # ランタイムを作成 print(lua.lua_implementation) # => Lua 5.4 print(lua.eval('1+1')) # evalメソッドでインラインでLuaのコードを実行可能 # => 2
基本的な使い方はLuaのランタイムを作成し、それを使ってLuaのコードを評価していくといった流れです。 ちなみに、ランタイムはLuaJITにも対応しています。 自分の環境ではLua5.4が使用されていました。
スクリプトのロード
Lupaではrequire
メソッドを利用して別ファイルで定義されたスクリプトを呼び出すことも可能です。
sum, path = lua.require('sum') print(path) # => ./sum.lua t = lua.table_from([1, 2, 3, 4]) print(sum(t)) # => 10
require
はスクリプトの返り値とそのパスを返します。スクリプトを呼び出すときはパスではなくLuaのrequireと同じ値で呼び出します。(require(./sum.lua)
ではエラーになる)
スクリプトの中身は以下のような感じです。
function sum(list) local ret = 0 for _, v in ipairs(list) do ret = ret + v end return ret end return sum
コルーチンの呼び出し
Luaのコルーチンを呼んでみます。 今回は値を足していくような関数をLuaで用意しました。
function accumulate() local val = coroutine.yield() while true do local new_val = coroutine.yield(val) val = val + new_val end end return accumulate
acc, path = lua.require('acc') co = acc.coroutine() co.send(None) for i in range(4): print(co.send(i)) # => 0 # 1 # 3 # 6
Lupaでは関数オブジェクトにはcoroutine
メソッドが実装されており、そこからコルーチンオブジェクトを作成可能です。
coroutine
メソッドの引数は関数の引数になります。
その後、作成されたコルーチンオブジェクトのsend
メソッドを使用することでコルーチンとの値のやり取りが可能になります。
最初にNone
を渡してあげるとコルーチンがスタートします。
OOP
Luaではクラスのようなものを書くことも可能です。
今回はDogという簡単なクラスを用意しました。
local Dog = {} function Dog:new(o) o = o or {} setmetatable(o, self) self.__index = self return o end function Dog:bow() return self.word end function Dog:multi_bow(num) for i = 1, num do coroutine.yield(i .. ": " .. self.word) end end return Dog
dog, path = lua.require('dog') buddy = dog.new(dog, lua.table_from({"word": "I'm buddy."})) print(dog.bow(buddy)) # => I'm buddy. co = dog.multi_bow.coroutine(buddy, 3) while True: try: print(co.send(None)) # => 1: I'm buddy. # 2: I'm buddy. # 3: I'm buddy. except StopIteration: break
ここで注目すべきはメソッドの呼び出し方です。
Luaの:
オペレータは隠れている第一引数のself
を省略するように働きます。
Lupaではこの省略を省けないので毎回、自分自身を第一引数に渡して上げる必要があります。
イメージとしては以下のような感じです。
function Dog:bow() <=> function Dog.bow(self)
これはコルーチンオブジェクトを作成する場合も同じです。 ただし、作成されたコルーチンオブジェクトには自分自身を渡す必要はありません。
最後に
今回はLupaを使う上で個人的に詰まった部分を解説しました。 PythonからLuaを呼び出す上でとても便利なパッケージだと思います。